// SPDX-License-Identifier: Mulan PSL v2
/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This software is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use std::{
    fmt,
    ops::{Deref, DerefMut},
};

/// A generic wrapper to enforce cache-line alignment to prevent False Sharing.
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(target_arch = "x86_64", repr(C, align(64)))]
#[cfg_attr(not(target_arch = "x86_64"), repr(C, align(128)))]
pub struct CacheAligned<T>(pub T);

impl<T> CacheAligned<T> {
    /// Creates a new cache-aligned wrapper around the value.
    #[inline]
    pub const fn new(value: T) -> Self {
        Self(value)
    }

    /// Returns a raw pointer to the inner value.
    #[inline]
    pub fn as_ptr(&self) -> *const T {
        &self.0 as *const T
    }

    /// Returns a mutable raw pointer to the inner value.
    #[inline]
    pub fn as_mut_ptr(&mut self) -> *mut T {
        &mut self.0 as *mut T
    }

    /// Consumes the wrapper and returns the inner value.
    #[inline]
    pub fn into_inner(self) -> T {
        self.0
    }
}

impl<T> Deref for CacheAligned<T> {
    type Target = T;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T> DerefMut for CacheAligned<T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl<T> AsRef<T> for CacheAligned<T> {
    #[inline]
    fn as_ref(&self) -> &T {
        &self.0
    }
}

impl<T> AsMut<T> for CacheAligned<T> {
    #[inline]
    fn as_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

impl<T> From<T> for CacheAligned<T> {
    #[inline]
    fn from(t: T) -> Self {
        Self::new(t)
    }
}

impl<T: fmt::Debug> fmt::Debug for CacheAligned<T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<T: fmt::Display> fmt::Display for CacheAligned<T> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_size() {
        let raw_data = u64::MAX;
        let aligned = CacheAligned::new(raw_data);

        assert_eq!(size_of_val(&aligned), size_of::<CacheAligned<u64>>());
        assert!(size_of::<CacheAligned<u64>>() >= size_of::<u64>());

        // Test that alignment padding is correct
        #[cfg(target_arch = "x86_64")]
        assert_eq!(
            size_of::<CacheAligned<u64>>() % 64,
            0,
            "Size should be multiple of alignment"
        );

        #[cfg(not(target_arch = "x86_64"))]
        assert_eq!(
            size_of::<CacheAligned<u64>>() % 128,
            0,
            "Size should be multiple of alignment"
        );
    }

    #[test]
    fn test_alignment() {
        let raw_data = u64::MAX;
        let aligned = CacheAligned::new(raw_data);

        let ptr = aligned.as_ptr();
        let addr = ptr as usize;

        // Test alignment requirements
        #[cfg(target_arch = "x86_64")]
        {
            assert_eq!(
                addr % 64,
                0,
                "CacheAligned should be 64-byte aligned on x86_64"
            );
            assert_eq!(
                align_of::<CacheAligned<u64>>(),
                64,
                "Type alignment should be 64 bytes"
            );
        }

        #[cfg(not(target_arch = "x86_64"))]
        {
            assert_eq!(
                addr % 128,
                0,
                "CacheAligned should be 128-byte aligned on non-x86_64"
            );
            assert_eq!(
                align_of::<CacheAligned<u64>>(),
                128,
                "Type alignment should be 128 bytes"
            );
        }

        // Test that alignment is better than the inner type
        assert!(align_of_val(&aligned) >= align_of_val(&raw_data));
    }
}
